import {
  MathTool,
  ParameterObjectType,
  RENDERORDER,
  TOLERANCE,
} from '@amcax/base';
import { ctx2 } from '@app-cad/module';
import { Vector2tuple, Vector3 } from 'three-legacy';
import { AnnotationType, DimAngleAttrs, QuadrantType } from './types';
import {
  calculatePoint,
  getAngle,
  getDotProduct,
  getLineIntersectionTwo,
  isPointInVerticalRange,
  pointProjectOnLine,
  RETAIN,
} from '@app-cad/common/utils';
import DimBase from './DimBase';

export class DimAngleTool extends DimBase {
  parallelNotCross: boolean = false; // 是否平行 且 没有交点
  center: Vector3; // 本地坐标 交点
  mouseXY: Vector3; // 鼠标点 本地坐标
  radius: number; // 圆弧半径
  relPos: Vector2tuple[]; // 相对位置
  arrowVecs: Vector3[] = []; // 箭头向量
  relPositon: Vector3; // 箭头相对位置
  crossInfo: any; // 交点信息
  // -------------------------------------------
  action: 'create' | 'update' = 'create'; // 创建或更新
  curvePoints: [Vector3, Vector3][]; // 线段
  linesVecs: Vector3[]; // 线段向量
  verticalVecs: Vector3[]; // 线段垂直向量
  guideLinesPoints: Vector3[] = []; // 辅助线
  arcVecs: Vector3[]; // 圆弧向量
  angles: Vector2tuple; // 圆弧角度
  lineAngle: number; // 两条线段的角度 （逆时针）
  // -------------------------------------------
  quadrant: QuadrantType = 1; // 1 第一象限 2 第二象限 3 第三象限 4 第四象限
  inputAngle: number = 0; // 输入的角度
  nowQuadrant: QuadrantType = 1; // 当前象限当更新标注时使用 即：action = 'update'时
  constructor(attrs: DimAngleAttrs) {
    super();
    this.setUserData({ type: AnnotationType.AngleAnno });
    this.init(attrs);
  }
  init(attrs: DimAngleAttrs) {
    this.updateDisableIfCurvesFixed(attrs.curveIds);
    this.update(attrs);
    this.setRenderOrder(RENDERORDER.LINE);
    this.setNormalColor();
  }
  update(attrs: DimAngleAttrs) {
    this.setDimArcInfo(attrs);
    this.updateArcLine(attrs);
    if (this.action === 'update' && !this.parallelNotCross) {
      this.updateArcExtra();
    }
    this.relPos = this.getPosLv();
  }
  defaultMouseXY() {
    // 获取中点
    const [p3, p4] = this.getLineCenter(this.curvePoints); // 获取线段中点
    const len = (p3.distanceTo(this.center) + p4.distanceTo(this.center)) / 2;
    this.mouseXY = this.getCenterVec(this.guideLinesPoints, this.center)
      .setLength(len)
      .add(this.center);
    this.radius = this.mouseXY.distanceTo(this.center); // 获取半径
    this.getGuideLinesPoints();
  }
  defaultPos() {
    const [p3, p4] = this.getLineCenter(this.curvePoints); // 获取线段中点
    this.mouseXY = new Vector3().addVectors(p3, p4).divideScalar(2);
  }
  // 获取line中点
  getLineCenter(lines: [Vector3, Vector3][]) {
    return lines.map((line) => {
      const [p1, p2] = line;
      return new Vector3().addVectors(p1, p2).divideScalar(2);
    });
  }
  getCenterVec(points, centerP) {
    const [p1, p2] = points;
    const v1 = new Vector3().subVectors(p1, centerP); // 向量1
    const v2 = new Vector3().subVectors(p2, centerP); // 向量2
    return v1.add(v2).divideScalar(2);
  }
  zoomUpdate() {
    if (this.action === 'update') {
      this.setLabelAndLine(null, 'zoom');
      ctx2.viewer.requestRender();
    }
  }
  // 创建圆弧标注
  updateArcLine(attrs: DimAngleAttrs) {
    this.createUpdateArcs();
    this.setLabelAndLine(attrs);
  }
  getRealDeg() {
    return this.inpAnIsReflex() ? 360 - this.getDeg() : this.getDeg();
  }
  setLabelAndLine(attrs?: DimAngleAttrs, state = 'update') {
    const ps = this.extendLine([this.mouseXY, this.center], 'plus', 'arc');
    const labelP = this.action === 'update' ? ps[1] : this.mouseXY;
    this.createUpdateLabel(
      attrs,
      labelP,
      this.inputAngle || this.getRealDeg(),
      ParameterObjectType.Angle,
    );
    if (!this.parallelNotCross) {
      this.createUpdateLines();
    } else {
      this.parallelNotCrossUpdate(state);
      this.createUpdateLines();
      this.createUpdateLine('line3', [...this.guideLinesPoints, this.mouseXY]);
    }
    this.arrow();
  }
  parallelNotCrossUpdate(state) {
    // 点到直线的投影
    const vec = this.linesVecs[0];
    const line1StarP = this.curvePoints[0][0];
    const line2StarP = this.curvePoints[1][0];
    this.guideLinesPoints[0] = this.getProjectP([line1StarP, vec]);
    this.guideLinesPoints[1] = this.getProjectP([line2StarP, vec]);
    if (state === 'zoom') {
      return;
    }
    if (this.inputAngle === 180) {
      this.arrowVecs[0] = this.arrowVecs[0].negate();
    } else if (this.inputAngle === 360) {
      this.arrowVecs = this.arrowVecs.map((vec) => vec.negate());
    }
  }
  getProjectP([line1StarP, vec]) {
    return pointProjectOnLine<Vector3>(this.mouseXY, line1StarP, vec);
  }
  arrow = () => {
    this.updateArrow(this.guideLinesPoints, this.arrowVecs, 'vector');
  };
  // 辅助方法计算鼠标点与圆心连线到那条直线的角度最小
  getincludedAngle(points: Vector3[], center: Vector3) {
    const vec1 = new Vector3().subVectors(center, points[0]);
    const vec2 = new Vector3().subVectors(center, points[1]);
    const vec3 = new Vector3().subVectors(center, points[2]);
    let key = vec1.angleTo(vec2) < vec1.angleTo(vec3);
    if (this.inpAnIsReflex() || !this.isContraryQuadrant()) {
      key = !key;
    }
    return key ? 1 : 0;
  }
  // 是否是相对象限
  isContraryQuadrant() {
    return [3, 4, 1, 2][this.quadrant - 1] === this.nowQuadrant;
  }
  notNeedArcExtra(): boolean {
    return (
      (this.inpAnIsReflex() && !this.isContraryQuadrant()) ||
      (!this.inpAnIsReflex() && this.nowQuadrant === this.quadrant)
    );
  }
  // 创建额外的弧度，即标注位置不在当前记录象限
  updateArcExtra() {
    if (this.notNeedArcExtra()) {
      return this.removeLine(['line4']);
    }
    const angle3 = getAngle(this.mouseXY, this.center);
    const key = this.getincludedAngle(
      [this.mouseXY, ...this.guideLinesPoints],
      this.center,
    );
    const angles: Vector2tuple = [...this.angles];
    angles[key] = angle3;
    angles.sort((a, b) => a - b);
    const vertexs = this.getCreateArcVertex(angles);
    this.createUpdateLine('line4', vertexs); /*  */
  }
  // 获取两条线之间的夹角
  getDeg(angles?: number[], status: 'rad' | 'deg' = 'deg') {
    angles = angles || this.angles.sort((a, b) => a - b);
    let deg = angles[1] - angles[0];
    if (deg > Math.PI) {
      deg = angles[0] + (2 * Math.PI - angles[1]);
    }
    if (status === 'deg') {
      return MathTool.fixNumber(MathTool.radToDeg(deg), RETAIN);
    } else {
      return MathTool.fixNumber(deg, RETAIN);
    }
  }
  /**
   * @description 获取弧度的顶点
   * */
  createUpdateArcs() {
    const angle: Vector2tuple = [...this.angles];
    if (!this.crossInfo.isParallel) {
      angle.sort((a, b) => a - b);
    }
    const vertexs1 = this.getCreateArcVertex(angle, this.inpAnIsReflex()); // 获取弧度的顶点
    this.createUpdateLine('line3', vertexs1); // 创建弧度
  }
  // 当输入框中的角度值为优角
  inpAnIsReflex() {
    return this.inputAngle > 180;
  }
  /**
   * @description 创建辅助线
   * */
  createUpdateLines() {
    this.createLine('line1', 0); // 创建圆弧的第一条辅助线
    this.createLine('line2', 1); // 创建圆弧的第二条辅助线
  }
  createLine(key: 'line1' | 'line2', ind: 0 | 1) {
    // isPointInVerticalRange
    const point1 = this.guideLinesPoints[ind];
    // 判断点是否在直线上
    const isExist = isPointInVerticalRange(
      point1,
      this.curvePoints[ind][0],
      this.curvePoints[ind][1],
    );
    if (isExist) {
      this.removeLine([key]);
    } else {
      const midd1 = this.curvePoints[ind][0];
      const midd2 = this.curvePoints[ind][1];
      const l1 = point1.distanceToSquared(midd1);
      const l2 = point1.distanceToSquared(midd2);
      const ps = this.extendLine([point1, l1 > l2 ? midd2 : midd1]);
      this.createUpdateLine(key, ps);
    }
  }
  // 获取圆弧的所有点
  getCreateArcVertex(angles: Vector2tuple, aClockwise = false) {
    let [startAngle, endAngle] = [...angles];
    const diff = Math.abs(endAngle - startAngle);
    // 处理180度特殊情况，以鼠标为准决定圆弧方向
    if (Math.abs(diff - Math.PI) < TOLERANCE) {
      // 计算鼠标与圆心的角度
      const mouseAngle = Math.atan2(
        this.mouseXY.y - this.center.y,
        this.mouseXY.x - this.center.x,
      );
      // 判断鼠标角度是否在startAngle到endAngle之间
      // 归一化角度到[0, 2PI)
      const norm = (a: number) => (a + Math.PI * 2) % (Math.PI * 2);
      const s = norm(startAngle);
      const e = norm(endAngle);
      const m = norm(mouseAngle);
      // 判断m是否在s到e之间
      const inArc = s < e ? m > s && m < e : m > s || m < e;
      if (!inArc) {
        // 交换起止角度，使圆弧在鼠标侧
        [startAngle, endAngle] = [endAngle, startAngle];
      }
    } else if (endAngle - startAngle > Math.PI) {
      startAngle = angles[1];
      endAngle = angles[0];
    }
    if (endAngle > Math.PI * 2) {
      endAngle = endAngle - Math.PI * 2;
    }
    return this.getArcVertexs({
      x: this.center.x,
      y: this.center.y,
      radius: this.radius,
      startAngle,
      endAngle,
      clockWise: aClockwise,
    });
  }
  // 获取相对位置
  getPosLv(vc?: Vector3): Vector2tuple[] {
    const vec = vc || this.linesVecs[0];
    const vecVer = this.verticalVecs[0];
    const lineStarP1 = this.curvePoints[0][0];
    const sign1 = getDotProduct(lineStarP1, this.mouseXY, vec) >= 0 ? 1 : -1;
    // 点到直线的距离
    const yL = this.getVerticalDis([lineStarP1, vec, vecVer]);
    const sign2 = getDotProduct(lineStarP1, this.mouseXY, vecVer) >= 0 ? 1 : -1;
    // 点到直线垂直向量的距离
    const xL = this.getVerticalDis([lineStarP1, vecVer, vec]);
    const dimRelLength: Vector2tuple = [xL * sign1, yL * sign2];
    return [dimRelLength];
  }
  // 根据相对位置计算点
  getCalPointsByPos(dimRelLength) {
    const vec = this.linesVecs[0];
    const dimRelVec = this.verticalVecs[0];
    const startP = this.curvePoints[0][0].clone();
    const center = calculatePoint(dimRelVec, dimRelLength[1], startP);
    const mouseXY = calculatePoint(vec, dimRelLength[0], center);
    return mouseXY;
  }
  // 获取输入框的值
  getVal() {
    return this.dimLabel?.getText() != null
      ? this.dimLabel?.getText()
      : this.getRealDeg();
  }
  // 重置输入框的值
  resetDimLabel() {
    this.dimLabel.update({
      text: this.inputAngle,
    });
  }
  // 获取角度
  getDimDeg() {
    const deg = this.getVal();
    let returnDeg = 0;
    if (this.isReflexAngle()) {
      if (!this.isBet()) {
        returnDeg = 180 + deg;
      } else {
        returnDeg = 360 - deg;
      }
    } else {
      if (!this.isBet()) {
        returnDeg = 180 - deg;
      } else {
        returnDeg = deg;
      }
    }
    return returnDeg;
  }
  getDimArcInfo(): Vector2tuple[] {
    return [[this.quadrant, this.getVal()]];
  }
  isBet() {
    return [2, 4].includes(this.quadrant);
  }
  cloneVec(vecs: Vector3[]) {
    return vecs.map((vec) => vec.clone());
  }
  getQuadrantByLine() {
    const signs = [];
    this.verticalVecs?.forEach((vec, ind) => {
      const point = this.curvePoints[ind][0];
      const symb = getDotProduct(point, this.mouseXY, vec);
      signs.push(Math.sign(symb));
    });
    const sign = signs[0] + ',' + signs[1];
    if (this.action === 'create') {
      this.quadrant = this.getQuadrant(sign);
    } else {
      this.nowQuadrant = this.getQuadrant(sign);
    }
    this.getVecArrowVecsByQuadrant();
    this.getGuideLinesPoints();
  }
  getQuadrant(coordinate) {
    // (<180) 1、 1,1 -> 靠近 | L2 2、 -1,1 | 3、-1,-1 -> 靠近L1 | 4、1,-1
    // (>180) 1、 1,1 -> 靠近 | L1 2、 -1,1 | 3、-1,-1 -> 靠近L2 | 4、1,-1
    // 第二象限 辅助线段和线段方向相反， 第三象限 辅助线段和线段方向相同
    switch (coordinate) {
      case '1,1':
        return 1;
      case '1,-1':
        return this.isReflexAngle() ? 2 : 4;
      case '-1,-1':
        return 3;
      case '-1,1':
        return this.isReflexAngle() ? 4 : 2;
      default:
        return 1;
    }
  }
  getVecArrowVecsByQuadrant() {
    const key = this.isReflexAngle() ? [1, 0] : [0, 1];
    const [v1, v2] = this.cloneVec(this.linesVecs);
    const [vec1, vec2] = this.cloneVec(this.verticalVecs);
    this.arcVecs = [v1, v2];
    this.arrowVecs = [vec1, vec2];
    switch (this.quadrant) {
      case 1:
        this.arcVecs[key[0]].negate();
        break;
      case 2:
        this.arcVecs = [v1.negate(), v2.negate()];
        this.arrowVecs[key[0]].negate();
        break;
      case 3:
        this.arcVecs[key[1]].negate();
        this.arrowVecs = [vec1.negate(), vec2.negate()];
        break;
      case 4:
        this.arrowVecs[key[1]].negate();
        break;
    }
  }
  isReflexAngle() {
    if (this.inputAngle >= 180) {
      return this.lineAngle < 180;
    } else {
      return this.lineAngle > 180;
    }
  }
  // 获取直线的辅助点
  getGuideLinesPoints() {
    this.guideLinesPoints = [];
    const angles: Vector2tuple = [0, 0];
    this.arcVecs?.forEach((vec, ind) => {
      const p = calculatePoint(vec, this.radius, this.center); // 计算圆上一点
      this.guideLinesPoints.push(p);
      angles[ind] = getAngle(p, this.center);
    });
    this.angles = angles;
  }
  setDimArcInfo(attrs: DimAngleAttrs) {
    if (attrs.action) {
      this.action = attrs.action;
    }
    this.setUserData({
      objects: attrs.curveIds,
      type: attrs.dimConstraint,
    });
    if (attrs.relPos) {
      this.userData.points = attrs.relPos;
    }
    if (attrs?.relPos?.length === 2) {
      this.action = 'update';
      this.quadrant = attrs.relPos[1][0] as QuadrantType;
      this.inputAngle = attrs.relPos[1][1];
    }
    if (attrs?.lineVertices?.length === 2) {
      //  获取线段点坐标
      this.verticesTransform(attrs.lineVertices);
      //  获取线段交点
      this.getCrossPoint([...attrs.lineVertices[0], ...attrs.lineVertices[1]]);
    }
    let flag = false;
    if (attrs.labelPos) {
      this.mouseXY = attrs.labelPos;
    } else if (attrs.relPos?.length > 0) {
      this.mouseXY = this.getCalPointsByPos(attrs.relPos[0]);
    } else {
      this.defaultPos(); // 确定默认象限
      flag = true;
    }
    this.radius = this.mouseXY.distanceTo(this.center); // 获取半径
    this.getQuadrantByLine(); // 获取象限、圆弧向量、圆弧角度、箭头向量
    if (flag) {
      this.defaultMouseXY();
    }
  }
  /**
   * @description 获取交点
   * @param {Vector2tuple[]} point1
   * */
  getCrossPoint(points: Vector2tuple[]) {
    // 两条辅助线求取交点
    const ps = points.map((p) => ({ x: p[0], y: p[1] }));
    const crossInfo = getLineIntersectionTwo(ps[0], ps[1], ps[2], ps[3]);
    this.removeLine(['line3', 'line4']);
    if (!crossInfo.x && !crossInfo.y && crossInfo.isParallel) {
      this.parallelNotCross = true;
      this.center = new Vector3(ps[0].x, ps[0].y, 0);
    } else {
      this.parallelNotCross = false;
      this.center = new Vector3(crossInfo.x, crossInfo.y, 0);
    }
    this.crossInfo = crossInfo;
  }
  // 获取线段vector3点坐标 和线段向量
  verticesTransform(lineVertices) {
    const angles = [];
    this.linesVecs = []; // 线段向量
    this.verticalVecs = []; // 线段垂直向量
    this.curvePoints = lineVertices.map((item) => {
      const ps = item.map((it) => new Vector3(it[0], it[1], 0));
      const vec = new Vector3().subVectors(ps[1], ps[0]).normalize();
      this.verticalVecs.push(new Vector3(-vec.y, vec.x, 0));
      this.linesVecs.push(vec);
      angles.push(getAngle(ps[1], ps[0]));
      return ps;
    });
    const angleC = angles[0] + Math.PI * 2 - angles[1];
    // 逆时针计算线段1到线段2的角度
    this.lineAngle = MathTool.radToDeg(
      2 * Math.PI - (angleC > Math.PI * 2 ? angleC - 2 * Math.PI : angleC),
    );
  }
}
